
/* GCSx
** IMGSELECT.CPP
**
** Image/tile/sprite selection toolbar
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
** 
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#include "all.h"

// Image struct

Image::Image() { start_func
    // Ensure our constants match layer constants
    assert((int)COLOR_BITDEPTH == (int)Layer::LAYER_TILE_COLOR_BITDEPTH);
    assert((int)COLOR_DEFAULT == (int)(Layer::LAYER_TILE_COLOR >> Layer::LAYER_TILE_COLOR_SHIFT));
    assert((int)ALPHA_BITDEPTH == (int)Layer::LAYER_EXT_ALPHA_BITDEPTH);
    assert((int)ALPHA_DEFAULT == (int)(Layer::LAYER_EXT_ALPHA >> Layer::LAYER_EXT_ALPHA_SHIFT));

    index = 0;
    color = COLOR_DEFAULT;
    alpha = ALPHA_DEFAULT;
    effects = 0;
}

// Pattern struct

Pattern::Pattern() : images() { start_func
    count = 0;
    width = 0;
    height = 0;
}

// Shown Images struct

ShownImages::ShownImages(const TileSetEdit* tiles) : strip(), recent() { start_func
    if ((tiles) && (tiles->getCount())) strip.index = 1;
}

// ImageSelect

const string ImageSelect::wtButtonPrevH("l");
const string ImageSelect::wtButtonNextH("k");
const string ImageSelect::wtButtonPrevV("m");
const string ImageSelect::wtButtonNextV("n");
const string ImageSelect::wtButtonExpand("q");
int ImageSelect::buttonWidth = 0;

map<const void*,ShownImages>* ImageSelect::shownBank = NULL;

void ImageSelect::destroyGlobals() { start_func
    if (shownBank) {
        delete shownBank;
        shownBank = NULL;
    }
}

ImageSelect::ImageSelect(TileSetEdit* tiles, const ColorStore* cStorage, int useExtendedFeatures) throw_File : Window(), shown(tiles) { start_func
    myFrame = NULL;
    buttonWidth = max(fontWidth(wtButtonExpand, FONT_WIDGET), fontHeight(FONT_WIDGET)) + 8;
    colors = cStorage;
    
    extendedFeatures = useExtendedFeatures;

    if (extendedFeatures) selectedType = SELECTED_NONE;
    else selectedType = SELECTED_STRIP;
    dragType = DRAG_NONE;
    selectedIndex = 0;
    useAlpha = 0;
    numImagesShownCount = IMAGE_COUNT;
    numImagesShownAcross = IMAGE_ACROSS;

    tileset = NULL;
    changeSet(tiles);
}

FrameWindow* ImageSelect::createWindowed() { start_func
    // Prevent duplication
    if (myFrame) {
        return myFrame;
    }

    // We remember the frame pointer even though it'll delete itself
    myFrame = new FrameWindow("Images", FrameWindow::RESIZING_SNAP, FrameWindow::FRAMETYPE_DIALOG, this, FrameWindow::TITLEBAR_TOOL, 0);
    return myFrame;
}

ImageSelect::~ImageSelect() { start_func
    if (tileset) tileset->markUnlock();
}

void ImageSelect::allowAlpha(int newUseAlpha) { start_func
    useAlpha = newUseAlpha;
    colorRefresh();
}

void ImageSelect::changeSet(TileSetEdit* tiles, int oldDeleted) throw_File { start_func
    if (tiles) {
        tiles->markLock(); // Exception point
        numImages = tiles->getCount();
        imageWidth = tiles->getWidth();
        imageHeight = tiles->getHeight();
    }
    else {
        numImages = 0;
        imageWidth = 32;
        imageHeight = 32;
    }
    
    // Unlock previous tileset
    if ((tileset) && (!oldDeleted)) tileset->markUnlock();

    // Always work as if tiles could be NULL
    tileset = tiles;
    
    zoomWidth = imageWidth;
    zoomHeight = imageHeight;
    
    if (zoomWidth > IMAGE_MAX_SIZE) {
        zoomHeight = imageHeight * IMAGE_MAX_SIZE / imageWidth;
        zoomWidth = IMAGE_MAX_SIZE;
    }

    if (zoomHeight > IMAGE_MAX_SIZE) {
        zoomWidth = imageWidth * IMAGE_MAX_SIZE / imageHeight;
        zoomHeight = IMAGE_MAX_SIZE;
    }
    
    paddedWidth = zoomWidth;
    paddedHeight = zoomHeight;
    
    if (paddedWidth < IMAGE_MIN_SIZE) paddedWidth = IMAGE_MIN_SIZE;
    if (paddedHeight < IMAGE_MIN_SIZE) paddedHeight = IMAGE_MIN_SIZE;
    
    // Find associated default values
    if (tiles) {
        if (!shownBank) shownBank = new map<const void*,ShownImages>;
        map<const void*,ShownImages>::iterator pos = shownBank->find(tiles);
        if (pos == shownBank->end()) {
            // No default values yet- create blanks
            ShownImages newShown(tiles);
            shown = newShown;
            shownBank->insert(pair<const void*,ShownImages>(tiles, newShown));
        }
        else {
            shown = (*pos).second;
        }
    }
    else {
        ShownImages newShown(NULL);
        shown = newShown;
    }
    
    resize(width, height);
    setDirty();
}

void ImageSelect::colorRefresh() { start_func
    int color = colors->fg.r | (colors->fg.g << 4) | (colors->fg.b << 8);
    int alpha = colors->fg.a;
    
    if (!useAlpha) alpha = Image::ALPHA_DEFAULT;

    shown.strip.color = color;
    shown.strip.alpha = alpha;
    
    if ((selectedType == SELECTED_RECENT) && (selectedIndex < ShownImages::PATTERN_RECENT_MAX)) {
        vector<Pattern::PatternEntry>::iterator pos;
        vector<Pattern::PatternEntry>::iterator end = shown.recent[selectedIndex].images.end();
        for (pos = shown.recent[selectedIndex].images.begin(); pos != end; ++pos) {
            (*pos).image.color = color;
            (*pos).image.alpha = alpha;
        }
    }
    
    saveDef();    
    setDirty();
}

void ImageSelect::getDataExt(Uint32& data, Uint32& ext, Uint32& fx) const { start_func
    const Image* current;
    
    data = 0;

    if (selectedType == SELECTED_STRIP) current = &shown.strip;
    else if ((selectedType == SELECTED_RECENT) && (selectedIndex < ShownImages::PATTERN_RECENT_MAX)) {
        if (shown.recent[selectedIndex].count == 1) current = &shown.recent[selectedIndex].images[0].image;
        else {
            // @TODO: patterns
            data = Layer::LAYER_TILE_DEFAULT;
            ext = Layer::LAYER_EXT_DEFAULT;
            fx = Layer::LAYER_FX_DEFAULT;
            return;
        }
    }
    else {
        data = Layer::LAYER_TILE_DEFAULT;
        ext = Layer::LAYER_EXT_DEFAULT;
        fx = Layer::LAYER_FX_DEFAULT;
        return;
    }

    // image
    data = current->index;
    if (selectedType == SELECTED_STRIP) {
        data += selectedIndex;
        if (numImages) data = (data - 1) % numImages + 1;
    }
    // color
    data |= current->color << Layer::LAYER_TILE_COLOR_SHIFT;
    // orientation
    if (current->effects & Image::EFFECTS_FLIP) data |= Layer::LAYER_TILE_FLIP;
    if (current->effects & Image::EFFECTS_MIRROR) data |= Layer::LAYER_TILE_MIRROR;
    if (current->effects & Image::EFFECTS_ROTATE) data |= Layer::LAYER_TILE_ROTATE;
    // alpha
    ext |= current->alpha << Layer::LAYER_EXT_ALPHA_SHIFT;
    
    // @TODO: not determined: effects, collision data, animation
    fx = Layer::LAYER_FX_DEFAULT;
}

void ImageSelect::dragLocation(int mX, int mY, int& type, int& index) const { start_func
    type = DRAG_NONE;
    
    int imgSize = paddedWidth;
    int imgAcross = paddedHeight;
    if (showVertical) {
        swap(mX, mY);
        imgSize = paddedHeight;
        imgAcross = paddedWidth;
    }
    
    // Buttons have the 'max' height for now
    if (mY < IMAGE_SEPARATION) return;
    if (mY >= IMAGE_SEPARATION + buttonHeight) return;
    
    if (mX < buttonWidth) {
        type = DRAG_STRIP_MORE;
        return;
    }

    mX -= buttonWidth + IMAGE_SEPARATION;
    if (mX < 0) return;

    if (mX < buttonWidth) {
        type = DRAG_STRIP_PREV;
        return;
    }

    mX -= buttonWidth + IMAGE_SEPARATION;
    if (mX < 0) return;
    
    // Images as a grid
    int col = mX / (IMAGE_BEVEL * 2 + imgSize + IMAGE_SEPARATION);
    int colI = mX % (IMAGE_BEVEL * 2 + imgSize + IMAGE_SEPARATION);
    
    if (col < numImagesShownCount) {
        if (colI >= IMAGE_BEVEL * 2 + imgSize) return;

        int row = (mY - IMAGE_SEPARATION) / (IMAGE_BEVEL * 2 + imgAcross + IMAGE_SEPARATION);
        int rowI = (mY - IMAGE_SEPARATION) % (IMAGE_BEVEL * 2 + imgAcross + IMAGE_SEPARATION);
        
        if (row >= numImagesShownAcross) return;
        if (rowI >= IMAGE_BEVEL * 2 + imgAcross) return;

        type = SELECTED_STRIP;
        index = col + row * numImagesShownCount;
        return;
    }
    
    mX -= (IMAGE_BEVEL * 2 + imgSize + IMAGE_SEPARATION) * numImagesShownCount;
    if (mX < 0) return;
    
    if (mX < buttonWidth) {
        type = DRAG_STRIP_NEXT;
        return;
    }
    
    // (strip only?)
    if (!extendedFeatures) return;

    mX -= buttonWidth + IMAGE_GUTTER;
    if (mX < 0) return;
    
    if (mX < IMAGE_BEVEL * 2 + imgSize) {
        // ("none" is centered- ensure only center area is clicked)
        if ((mY >= blankTileCenter + IMAGE_SEPARATION) && (mY < blankTileCenter + IMAGE_BEVEL * 2 + imgAcross + IMAGE_SEPARATION)) type = SELECTED_NONE;
        return;
    }

    mX -= IMAGE_BEVEL * 2 + imgSize + IMAGE_GUTTER;
    if (mX < 0) return;

    if (mX < buttonWidth) {
        type = DRAG_RECENT_MORE;
        return;
    }

    mX -= buttonWidth + IMAGE_SEPARATION;
    if (mX < 0) return;
    
    // patterns as a grid
    col = mX / (IMAGE_BEVEL * 2 + imgSize + IMAGE_SEPARATION);
    colI = mX % (IMAGE_BEVEL * 2 + imgSize + IMAGE_SEPARATION);
    
    if (col < numPatternsShown) {
        if (colI >= IMAGE_BEVEL * 2 + imgSize) return;

        int row = (mY - IMAGE_SEPARATION) / (IMAGE_BEVEL * 2 + imgAcross + IMAGE_SEPARATION);
        int rowI = (mY - IMAGE_SEPARATION) % (IMAGE_BEVEL * 2 + imgAcross + IMAGE_SEPARATION);
        
        if (row >= numImagesShownAcross) return;
        if (rowI >= IMAGE_BEVEL * 2 + imgAcross) return;

        index = col + row * numPatternsShown;
        if (index >= ShownImages::PATTERN_RECENT_MAX) index = 0;
        else type = SELECTED_RECENT;
        return;
    }
}

int ImageSelect::event(int hasFocus, const SDL_Event* event) { start_func
    switch (event->type) {
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONDBL: {
            // @TODO: Double click special?
            if (event->button.button == SDL_BUTTON_LEFT) {
                int newType;
                int newIndex;
                
                dragLocation(event->button.x, event->button.y, newType, newIndex);
    
                if (newType >= DRAG_STRIP_PREV) {
                    prevDrag = dragType = newType;
                    if (dragType == DRAG_STRIP_PREV) prevImage();
                    else if (dragType == DRAG_STRIP_NEXT) nextImage();
                }
                else {
                    prevDrag = dragType = DRAG_NONE;
                    if (newType != DRAG_NONE) {
                        if (newType == SELECTED_STRIP) {
                            selectedType = SELECTED_STRIP;
                            selectedIndex = newIndex;
                        }
                        else if (newType == SELECTED_NONE) {
                            selectedType = SELECTED_NONE;
                        }
                        else if (newType == SELECTED_RECENT) {
                            selectedType = SELECTED_RECENT;
                            selectedIndex = newIndex;
                        }
                    }
                }
                
                setDirty();
                return 1;
            }
            
            if (event->button.button == SDL_BUTTON_WHEELUP) {
                prevImage();
                return 1;
            }
            
            if (event->button.button == SDL_BUTTON_WHEELDOWN) {
                nextImage();
                return 1;
            }            
            break;
        }
        
        case SDL_MOUSEBUTTONUP: {
            if (dragType != DRAG_NONE) {
                if (dragType == DRAG_RECENT_MORE) {
                    // @TODO:
                }
                else if ((dragType == DRAG_STRIP_MORE) && (numImages)) {
                    // @TODO: doesn't apply current color to image chooser
                    // @TODO: doesn't appear in a good area for vertical
                    ImageChooser* imgChoose = new ImageChooser(tileset);
                    int chosenTile = imgChoose->runModal(getScreenX(), getScreenY());
                    delete imgChoose;
                    if (chosenTile > 0) selectImage(chosenTile);
                }
        
                dragType = DRAG_NONE;
        
                setDirty();
                return 1;
            }
            break;
        }
        
        case SDL_MOUSEMOTION: {
            if (event->motion.state & SDL_BUTTON_LMASK) {
                int newType;
                int newIndex;
                
                dragLocation(event->button.x, event->button.y, newType, newIndex);
                
                if (newType == prevDrag) {
                    dragType = prevDrag;
                }
                else {
                    dragType = DRAG_NONE;
                }
                
                setDirty();
                return 1;
            }
            break;
        }
    }

    return 0;
}

void ImageSelect::nextImage() { start_func
    if (shown.strip.index >= numImages) shown.strip.index = 1;
    else ++shown.strip.index;
    
    saveDef();    
    setDirty();
}

void ImageSelect::prevImage() { start_func
    if (shown.strip.index <= 1) shown.strip.index = numImages;
    else --shown.strip.index;
    
    saveDef();    
    setDirty();
}

void ImageSelect::selectImage(int tile) { start_func
    selectedType = SELECTED_STRIP;
    // Reset to middle-est image
    selectedIndex = numImagesShownCount / 2 + (numImagesShownAcross / 2) * numImagesShownCount;

    if (numImages) {
        shown.strip.index = tile - selectedIndex;
        while (shown.strip.index <= 0) {
            shown.strip.index += numImages;
        }
    }
    else {
        shown.strip.index = 0;
    }
    
    saveDef();    
    setDirty();
}

void ImageSelect::saveDef() { start_func
    // Save defaults
    if ((tileset) && (shownBank)) {
        map<const void*,ShownImages>::iterator pos = shownBank->find(tileset);
        if (pos != shownBank->end()) (*pos).second = shown;
    }
}

void ImageSelect::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
    assert(destSurface);

    if (visible) {
        // If dirty, redraw all
        if (dirty) {
            getRect(toDisplay);
            toDisplay.x += xOffset;
            toDisplay.y += yOffset;
            dirty = 0;
            intersectRects(toDisplay, clipArea);
        }
        
        // Anything to draw?
        if (toDisplay.w) {
            SDL_SetClipRect(destSurface, &toDisplay);

            xOffset += x;
            yOffset += y;

            int imgSize = paddedWidth;
            int imgAcross = paddedHeight;
            if (showVertical) {
                imgSize = paddedHeight;
                imgAcross = paddedWidth;
            }
        
            // This toolbar only redraws images that are part of the dirty area
            SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
            
            // First image row- more, prev, images in grid, next
            int xPos = 0;
            int yPos = 0;
            xPos += drawButton(destSurface, xOffset, yOffset, xPos, yPos + IMAGE_SEPARATION, wtButtonExpand, dragType == DRAG_STRIP_MORE);
            xPos += IMAGE_SEPARATION;
            xPos += drawButton(destSurface, xOffset, yOffset, xPos, yPos + IMAGE_SEPARATION, showVertical ? wtButtonPrevV : wtButtonPrevH, dragType == DRAG_STRIP_PREV);
            
            Image img = shown.strip;
            int startX = xPos;
            for (int pos = 0; pos < numImagesShownCount * numImagesShownAcross; ++pos) {
                if (numImages) {
                    img.index = (shown.strip.index + pos - 1) % numImages + 1;
                    xPos += drawPattern(destSurface, xOffset, yOffset, xPos, yPos, (selectedType == SELECTED_STRIP) && (selectedIndex == pos), &img, NULL);
                }
                else {
                    xPos += drawBlankImage(destSurface, xOffset, yOffset, xPos, yPos, (selectedType == SELECTED_STRIP) && (selectedIndex == pos));
                }
                if ((pos % numImagesShownCount) == (numImagesShownCount - 1)) {
                    xPos = startX;
                    yPos += imgAcross + IMAGE_SEPARATION + IMAGE_BEVEL * 2;
                }
            }
            
            // Reset to "topmost"
            yPos = 0;
            xPos += (imgSize + IMAGE_SEPARATION + IMAGE_BEVEL * 2) * numImagesShownCount;
            
            // (next button)
            xPos += IMAGE_SEPARATION;
            xPos += drawButton(destSurface, xOffset, yOffset, xPos, yPos + IMAGE_SEPARATION, showVertical ? wtButtonNextV : wtButtonNextH, dragType == DRAG_STRIP_NEXT);
            
            if (extendedFeatures) {
                // Blank image (centered)
                xPos += IMAGE_GUTTER - IMAGE_SEPARATION;
                xPos += drawBlankImage(destSurface, xOffset, yOffset, xPos, yPos + blankTileCenter, selectedType == SELECTED_NONE);
                
                // Second image row- more, recents (grid)
                xPos += IMAGE_GUTTER;
                xPos += drawButton(destSurface, xOffset, yOffset, xPos, yPos + IMAGE_SEPARATION, wtButtonExpand, dragType == DRAG_RECENT_MORE);
                
                int startX = xPos;
                for (int pos = 0; pos < numPatternsShown * numImagesShownAcross; ++pos) {
                    if (pos >= ShownImages::PATTERN_RECENT_MAX) break;
                    xPos += drawPattern(destSurface, xOffset, yOffset, xPos, yPos, (selectedType == SELECTED_RECENT) && (selectedIndex == pos), NULL, &shown.recent[pos]);
                    if ((pos % numPatternsShown) == (numPatternsShown - 1)) {
                        xPos = startX;
                        yPos += imgAcross + IMAGE_SEPARATION + IMAGE_BEVEL * 2;
                    }
                }
                
                // (remember to reset to "topmost" if we ever have anything after patterns
            }
        }
    }
}

int ImageSelect::drawButton(SDL_Surface* destSurface, int baseX, int baseY, int offsX, int offsY, const string& wText, int pressed) const { start_func
    // @TODO: check if button intercepts clip area
    int offset = 0;
    
    int w, h, x, y;
    if (showVertical) {
        w = buttonHeight;
        h = buttonWidth;
        x = baseX + offsY;
        y = baseY + offsX;
    }
    else {
        w = buttonWidth;
        h = buttonHeight;
        x = baseX + offsX;
        y = baseY + offsY;
    }
    
    if (pressed) {
        // Non-gradient: SDL_FillRect(...);
        drawGradient(x, y, w, h, guiRGB[COLOR_BUTTONDOWN1], guiRGB[COLOR_BUTTONDOWN2], destSurface);
        drawGuiBoxInvert(x, y, w, h, 2, destSurface);
        offset = 1;
    }
    else {
        drawGuiBoxInvert(x, y, w, h, 1, destSurface);
        drawGuiBox(x + 1, y + 1, w - 2, h - 2, 2, destSurface);
        // Non-gradient: nothing
        drawGradient(x + 3, y + 3, w - 6, h - 6, guiRGB[COLOR_BUTTONFILL1], guiRGB[COLOR_BUTTONFILL2], destSurface);
    }
    
    drawText(wText, guiRGB[COLOR_TEXT], x + (w - fontWidth(wText, FONT_WIDGET)) / 2 + offset, y + (h - fontHeight(FONT_WIDGET)) / 2 + offset, destSurface, FONT_WIDGET);
    
    return buttonWidth;
}

int ImageSelect::drawBlankImage(SDL_Surface* destSurface, int baseX, int baseY, int offsX, int offsY, int selected) const { start_func
    // @TODO: check if button intercepts clip area
    int x, y;
    if (showVertical) {
        x = baseX + offsY;
        y = baseY + offsX;
    }
    else {
        x = baseX + offsX;
        y = baseY + offsY;
    }
    
    if (selected) {
        drawGuiBoxInvert(x, y,
                         paddedWidth + IMAGE_SEPARATION * 2 + IMAGE_BEVEL * 2,
                         paddedHeight + IMAGE_SEPARATION * 2 + IMAGE_BEVEL * 2,
                         IMAGE_BEVEL, destSurface);
        drawGradient(x + IMAGE_BEVEL, y + IMAGE_BEVEL,
                     paddedWidth + IMAGE_SEPARATION * 2,
                     paddedHeight + IMAGE_SEPARATION * 2,
                     guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
    }
    else {
        drawGuiBoxInvert(x + IMAGE_SEPARATION, y + IMAGE_SEPARATION,
                         paddedWidth + IMAGE_BEVEL * 2, paddedHeight + IMAGE_BEVEL * 2,
                         IMAGE_BEVEL, destSurface);
    }
             
    x += IMAGE_SEPARATION + IMAGE_BEVEL;
    y += IMAGE_SEPARATION + IMAGE_BEVEL;
    drawRect(x, y,
             paddedWidth, paddedHeight,
             guiPacked[COLOR_TRANSPARENT1], destSurface);
    drawLine(x, y, x + paddedWidth - 1, y + paddedHeight - 1,
             guiPacked[COLOR_TRANSPARENT2], destSurface);
    drawLine(x + paddedWidth - 1, y, x, y + paddedHeight - 1,
             guiPacked[COLOR_TRANSPARENT2], destSurface);
    
    return (showVertical ? paddedHeight : paddedWidth) + IMAGE_SEPARATION + IMAGE_BEVEL * 2;
}

int ImageSelect::drawPattern(SDL_Surface* destSurface, int baseX, int baseY, int offsX, int offsY, int selected, Image* image, Pattern* pattern) const { start_func
    // @TODO: check if pattern intercepts clip area
    assert(pattern || (image && (image->index)));
    int x, y;
    if (showVertical) {
        x = baseX + offsY;
        y = baseY + offsX;
    }
    else {
        x = baseX + offsX;
        y = baseY + offsY;
    }
    
    if (selected) {
        drawGuiBoxInvert(x, y,
                         paddedWidth + IMAGE_SEPARATION * 2 + IMAGE_BEVEL * 2,
                         paddedHeight + IMAGE_SEPARATION * 2 + IMAGE_BEVEL * 2,
                         IMAGE_BEVEL, destSurface);
        drawGradient(x + IMAGE_BEVEL, y + IMAGE_BEVEL,
                     paddedWidth + IMAGE_SEPARATION * 2,
                     paddedHeight + IMAGE_SEPARATION * 2,
                     guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
    }
    else {
        drawGuiBoxInvert(x + IMAGE_SEPARATION, y + IMAGE_SEPARATION,
                         paddedWidth + IMAGE_BEVEL * 2, paddedHeight + IMAGE_BEVEL * 2,
                         IMAGE_BEVEL, destSurface);
    }

    drawRect(x + IMAGE_SEPARATION + IMAGE_BEVEL, y + IMAGE_SEPARATION + IMAGE_BEVEL,
             paddedWidth, paddedHeight,
             SDL_MapRGB(destSurface->format, 0, 0, 0), destSurface);
                                                                         
    if ((pattern) && (pattern->count == 1)) image = &pattern->images[0].image;
    
    if (image) {
        tileset->blitTileFx(image->index, destSurface,
                            x + IMAGE_SEPARATION + IMAGE_BEVEL, y + IMAGE_SEPARATION + IMAGE_BEVEL,
                            image->alpha, image->effects & Image::EFFECTS_MIRROR,
                            image->effects & Image::EFFECTS_FLIP,
                            image->effects & Image::EFFECTS_ROTATE, image->color);
    }
    else {
        // @TODO: show pattern here
    }
    
    return (showVertical ? paddedHeight : paddedWidth) + IMAGE_SEPARATION + IMAGE_BEVEL * 2;
}

Window::WindowType ImageSelect::windowType() const { start_func
    return WINDOW_CLIENT;
}

const char* ImageSelect::tooltip(int xPos, int yPos) const { start_func
    return "@TODO: tooltips (account for vertical)";
}

void ImageSelect::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
    if (newViewWidth == -1) newViewWidth = viewWidth;
    if (newViewWidth != -1) newWidth = newViewWidth;
    
    if (newViewHeight == -1) newViewHeight = viewHeight;
    if (newViewHeight != -1) newHeight = newViewHeight;
    
    int minReq = 0;
    
    if (newHeight > newWidth) showVertical = 1;
    else showVertical = 0;

    // Swap?
    int imgSize = paddedWidth;
    int imgAcross = paddedHeight;
    if (showVertical) {
        swap(newWidth, newHeight);
        imgSize = paddedHeight;
        imgAcross = paddedWidth;
    }
    
    if (extendedFeatures) {
        int patternArea = newWidth;

        // layout- btn|sep|btn|sep|(pad|sep x1+)|btn|gutter|pad|gutter|btn|(sep|pad x0+)
        // All required elements (4 buttons, blank image, all required seps and gutters)
        minReq = buttonWidth * 4 + IMAGE_GUTTER * 2 + imgSize + IMAGE_BEVEL * 2 + IMAGE_SEPARATION * 2;
        patternArea -= minReq;

        // First set of images goes to regular images
        numImagesShownCount = patternArea / (imgSize + IMAGE_BEVEL * 2 + IMAGE_SEPARATION);
        if (numImagesShownCount < 1) numImagesShownCount = 1;
        // Any extras go to patterns
        if (numImagesShownCount > IMAGE_COUNT) {
            numPatternsShown = numImagesShownCount - IMAGE_COUNT;
            numImagesShownCount = IMAGE_COUNT;
        }
        else {
            numPatternsShown = 0;
        }
    }
    else {
        int imagesArea = newWidth;
        minReq = buttonWidth * 3 + IMAGE_SEPARATION * 2;
        imagesArea -= minReq;
    
        numImagesShownCount = imagesArea / (imgSize + IMAGE_BEVEL * 2 + IMAGE_SEPARATION);
        if (numImagesShownCount < 1) numImagesShownCount = 1;
        
        numPatternsShown = 0;
    }
        
    numImagesShownAcross = (newHeight - IMAGE_SEPARATION) / (imgAcross + IMAGE_BEVEL * 2 + IMAGE_SEPARATION);
    if (numImagesShownAcross < 1) numImagesShownAcross = 1;

    newWidth = minReq + (numPatternsShown + numImagesShownCount) * (imgSize + IMAGE_BEVEL * 2 + IMAGE_SEPARATION);
    newHeight = IMAGE_SEPARATION +  numImagesShownAcross * (imgAcross + IMAGE_BEVEL * 2 + IMAGE_SEPARATION);
    buttonHeight = newHeight - IMAGE_SEPARATION * 2;
    blankTileCenter = (newHeight - imgAcross) / 2 - IMAGE_BEVEL - IMAGE_SEPARATION;

    // @TODO: attempt to retain the same selected item overall?
    if (selectedType == SELECTED_STRIP) {
        if (selectedIndex >= numImagesShownCount * numImagesShownAcross)
            selectedIndex = numImagesShownCount * numImagesShownAcross - 1;
    }
    else if (selectedType == SELECTED_RECENT) {
        if (selectedIndex >= numPatternsShown * numImagesShownAcross)
            selectedIndex = numPatternsShown * numImagesShownAcross - 1;
    }
    
    // Swap back?
    if (showVertical) swap(newWidth, newHeight);

    // Recurse back to parent until we get a match
    if ((newWidth != width) || (newHeight != height)) fromParent = 0;    
    Window::resize(newWidth, newHeight, newViewWidth, newViewHeight, fromParent);
}

